using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace GPSNav.NET
{

	public sealed class GpsRenderer
	{
		#region Singleton
		static readonly GpsRenderer instance = new GpsRenderer();
		static GpsRenderer()
		{
		}
		GpsRenderer()
		{
			Initialize();
		}
		public static GpsRenderer Instance
		{
			get
			{
				return instance;
			}
		}
		#endregion




		List<Color> cols = new List<Color>();
		List<short> wids = new List<short>();

		private void Initialize()
		{


			/*
			 ods.Add("motorway", 1);
			lods.Add("motorway_link", 1);
			lods.Add("trunk", 2);
			lods.Add("trunk_link", 2);
			lods.Add("primary", 3);
			lods.Add("primary_link", 3);
			lods.Add("secondary", 4);
			lods.Add("tertiary", 5);
			lods.Add("road", 6);
			lods.Add("residential", 7);
			lods.Add("living_street", 8);
			lods.Add("service", 9);
			lods.Add("track", 10);
			lods.Add("unclassified", 10);
			lods.Add("pedestrian", 11);
			lods.Add("services", 12);
			lods.Add("bus_guideway", 13);
			 */
			cols.Add(Color.Black);

			cols.Add(Color.FromArgb(255, Color.RoyalBlue));
			cols.Add(Color.FromArgb(255, Color.MediumSeaGreen));
			cols.Add(Color.FromArgb(255, Color.LightCoral));
			cols.Add(Color.FromArgb(255, Color.SandyBrown));
			cols.Add(Color.GreenYellow);
			cols.Add(Color.HotPink);
			cols.Add(Color.Khaki);
			cols.Add(Color.Silver);
			cols.Add(Color.Sienna);
			cols.Add(Color.LightBlue);
			cols.Add(Color.Firebrick);
			cols.Add(Color.Green);
			cols.Add(Color.Yellow);


			wids.Add(0);

		}










		/// <summary>
		/// 
		/// </summary>
		/// <param name="gpsMap"><see cref="GpsMap"/></param>
		/// <param name="lod">level of detail 1->128TBD</param>
		/// <param name="zoom">zoom level 1->128TBD</param>
		/// <param name="nodeId">center node</param>
		/// <param name="g"><see cref="System.Drawing.Graphics"/></param>
		/// <param name="rect"><see cref="System.Drawing.Rectangle"/></param>
		//public static void DrawGraph(GpsMap gpsMap, int lod, int zoom, Int64 nodeId, Graphics g, Rectangle rect)

		public void DrawGraph(GpsMap gpsMap, int lod, float zoom, GeoPoint loc, Graphics g, Rectangle rect)
		{

			bool dotext = false;

			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
			double mainScale = 100000;// scale 1:1 <=> 1gr=100px;//interesting 10000 vs 1000 works better faster, maybe something to do with lod and zoom
			double scale = mainScale * zoom;


			double w = rect.Width / (scale / 2);
			double h = rect.Height / (scale / 2);
			double lat = loc.Lat - w / 2;
			double lon = loc.Lon - h / 2;

			double w2 = 4 * w;
			double h2 = 4 * h;
			double lat2 = lat - w;
			double lon2 = lon - h;

			Matrix mm = new Matrix();
			mm.Scale((float) scale, (float) scale, MatrixOrder.Append);
			mm.Translate(rect.Height / 2, rect.Width / 2, MatrixOrder.Append);

			GeoRectangle r;

			r = new GeoRectangle(lat, lon, w, h);
			GpsWaysCollection ways = gpsMap.GetWays(r, lod, "<=");
			SortedDictionary<int, GraphicsPath> gps = new SortedDictionary<int, GraphicsPath>();
			GraphicsPath textGpath = new GraphicsPath();
			foreach (GpsWay way in ways)
			{
				if (way.LOD < 0)
					continue;
				if (!gps.ContainsKey(way.LOD))
				{
					gps.Add(way.LOD, new GraphicsPath());
				}

				//List<PointF> points = new List<PointF>();
				GeoRectangle tr = GeoRectangle.Intersect(way.Bounds, r);
				bool lookFor = (way.StringData.Trim() != "") && (tr != GeoRectangle.Empty);
				double md = Double.PositiveInfinity;
				int idxNode = -1;


				PointF [] pointsArray = new PointF [way.Nodes.Count];
				int k = 0;
				foreach (GpsNode node in way.Nodes)
				{
					float x = -(float) (loc.Lon - node.Location.Lon);
					float y = (float) (loc.Lat - node.Location.Lat);
					pointsArray [k]=new PointF(x, y);
					//points.Add(new PointF(x, y));

					//look for closest path point for text 
					if (dotext && lookFor)
					{
						double cd = node.Location ^ tr.Middle();
						if (md > cd)
						{
							md = cd;
							//idxNode = points.Count - 1;
							idxNode = k;
						}
					}

					k++;
				}

				//PointF [] pointsArray = points.ToArray();
				GraphicsPath gp = gps [way.LOD];

				GraphicsPath xxx = new GraphicsPath();
				xxx.AddLines(pointsArray);
				xxx.Transform(mm);


				gp.StartFigure();
				gp.SetMarkers();
				gp.AddPath(xxx, false);

				if (dotext  && idxNode >= 0)
				{
					GraphicsPath tPath = new GraphicsPath();
					tPath = Utils.MakeTextOnPath(xxx.PathPoints, idxNode, way.StringData, FontFamily.GenericSansSerif, 0, 10, 1, -5);
					if (tPath != null && tPath.PointCount > 0)
						textGpath.AddPath(tPath, false);
				}
			}


			g.Clear(Color.WhiteSmoke);
			int [] sortLods = new int [gps.Count];
			gps.Keys.CopyTo(sortLods, 0);

			g.SetClip(rect);

			for (int i = sortLods.Length - 1; i >= 0; i--)
			{
				int cLod = sortLods [i];
				Pen p0 = new Pen(Color.Gray, 9 - cLod / 2);
				Pen p1 = new Pen(cols [cLod], 7 - cLod / 2);
				g.DrawPath(p0, gps [cLod]);
				g.DrawPath(p1, gps [cLod]);
			}
			


			Pen pt = new Pen(Color.White, 3);
			g.DrawPath(pt, textGpath);
			g.FillPath(Brushes.Black, textGpath);

			Console.WriteLine("done");


		}


		public void DrawGraph2(GpsMap gpsMap, int lod, float zoom, GeoPoint loc, Graphics g, Rectangle rect)
		{

			g.Clear(Color.White);
			double mainScale = 1000;// scale 1:1 <=> 1gr=100px;
			double scale = mainScale * zoom;
			//GpsNode n = gpsMap.GetNode(nodeId);
			/*double w = rect.Width / scale;
			double h = rect.Height / scale;
			double lat = n.Location.Lat - w / 2;
			double lon = n.Location.Lon - h / 2;*/

			double w = rect.Width / (scale / 2);
			double h = rect.Height / (scale / 2);
			double lat = loc.Lat - w / 2;
			double lon = loc.Lon - h / 2;

			GeoRectangle r = new GeoRectangle(lat, lon, w, h);


			Console.WriteLine(r);
			//GpsWaysCollection ways = gpsMap.GetWays(r, lod, ">=");
			GpsWaysCollection ways = gpsMap.GetWays(r, lod, "<=");
			Console.WriteLine(ways.Count);
			foreach (GpsWay way in ways)
			{
				//Console.WriteLine("way {0}/{1}",way.WayId, way.LOD);
				if (way.LOD < 0)
					continue;

				List<PointF> points = new List<PointF>();


				foreach (GpsNode node in way.Nodes)
				{
					//Console.WriteLine(node);
					float x = rect.Width - (float) (rect.Width / 2 - (loc.Lat * scale - node.Location.Lat * scale));
					float y = (float) (rect.Height / 2 - (loc.Lon * scale - node.Location.Lon * scale));
					//g.FillEllipse(Brushes.Red, x - 2, y - 2, 4, 4);

					points.Add(new PointF(y, x));



				}



				Pen p = new Pen(Color.Black, 7 - way.LOD / 2);
				Pen p2 = new Pen(cols [way.LOD], 6 - way.LOD / 2);

				PointF [] pointsArray = points.ToArray();
				g.DrawLines(p, pointsArray);
				g.DrawLines(p2, pointsArray);

				/*
				foreach (PointF pf in points)
				{
					g.FillEllipse(Brushes.Black, pf.X - 2, pf.Y - 2, 4, 4);
				}  */



			}


			Console.WriteLine("done");
			//look into waysBounds and extract only ways that intersect the rect
			//apply lod filtering


			/*
			List<GpsNode> nodes = gpsMap.GetNodes(r);
			//Console.WriteLine(nodes.Count);

			foreach (GpsNode nd in nodes)
			{
				GeoSize p = nd.Location - r.Location;
				p.Width = p.Width * scale;
				p.Height = p.Height * scale;
				//Console.WriteLine("{0} {1}", p.Width * scale, p.Height * scale);

				g.FillEllipse(Brushes.Black, (float) p.Width - 1, (float) p.Height - 1, 2, 2);

			}
			  */



			//get gpsMap wise bound rect (zoom,nodeId,[lod])
			//get gpsMap wise sectors intersect rect, zoom
			//foreach sector draw all nodes, edges


		}


	}
}
